﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using WooCoo.Tools;

namespace WooCoo.ObjectModel
{
    /// <summary>
    /// 实体仓库工厂类
    /// 用于创建指定实体的仓库,通过实体的FullName名寻找对应原Repository
    /// </summary>
    internal class RepositoryFactory : IRepositoryFactory
    {
        public static readonly RepositoryFactory Instance = new RepositoryFactory();

        //目前的Repository全部使用缓存
        private IDictionary<Type, IRepository> _all = new SortedDictionary<Type, IRepository>(TypeNameComparer.Instance);

        //使用这个字段高速缓存最后一次使用的Repository。
        private IRepository _lastRepository;
        //private Type _defaultRepositoryType;

        private RepositoryFactory() 
        {
        }

        /// <summary>
        /// 使用指定子类的Repository覆盖它的父类的Repository。
        /// 
        /// 这时，所有调用Create、Concreate方法为它的父类生成Repository时，都会返回指定子类的仓库
        /// </summary>
        /// <param name="baseType"></param>
        /// <param name="subType"></param>
        public void OverrideRepository(Type baseType, Type subType)
        {
            if (baseType == null) throw new ArgumentNullException("baseType");
            if (subType == null) throw new ArgumentNullException("subType");
            if (!baseType.IsAssignableFrom(subType)) throw new ArgumentException("subType 并不是 baseType 的子类，不能重写！");

            //重写前，必须保证这两个类型的Repository已经正常生成。
            var repositoryBase = this.Find(baseType);
            var repositorySub = this.Find(subType);

            //如果都正常生成，则直接覆盖。
            if (repositoryBase != null && repositorySub != null)
            {
                lock (this._all)
                {
                    this._all[baseType] = repositorySub;
                }
            }
        }

        /// <summary>
        /// 用于创建指定实体的仓库。
        /// </summary>
        /// <param name="entityType"></param>
        /// <returns></returns>
        internal IRepository Find(Type entityType)
        {
            var last = this._lastRepository;
            //if (last != null && last.EntityType == entityType) return last;

            IRepository result = null;

            if (!this._all.TryGetValue(entityType, out result))
            {
                lock (this._all)
                {
                    result = this.FindWithoutLock(entityType);
                }
            }

            this._lastRepository = result;

            return result;
        }

        private IRepository FindWithoutLock(Type entityType)
        {
            IRepository result = null;

            if (!this._all.TryGetValue(entityType, out result))
            {
                result = this.DoCreate(entityType);
                this._all.Add(entityType, result);
            }

            return result;
        }

        /// <summary>
        /// 创建一个实体类型的仓库。
        /// </summary>
        /// <param name="entityType"></param>
        /// <returns></returns>
        [Obsolete("类扩展，未实现，考虑以承继的方式扩展实体Repository类")]
        private IRepository DoCreate(Type entityType)
        {
            //先尝试在IOC容器中中寻找实体类型对应的仓库类型。需要考虑承继情况
            object val;
            if (WooCoo.IoC.Container.Current.TryGetExports(entityType.FullName, out val))
            {
                if (val is IRepository)
                    return val as IRepository;
                else
                    throw new NotSupportedException(Localizer.Active.GetLocalizeString(StringID.NoImpIRepository));
            }            
            //如果上面在约定中没有找到，则直接生成一个默认的实体仓库。
            return this.CreateDefaultRepository(entityType);
        }

        #region 生成默认Repository类

        /// <summary>
        /// 如果上面在约定中没有找到，则直接生成一个默认的实体仓库,将通过类型的命名空间名，去寻找对应的实体仓库
        /// </summary>
        /// <param name="entityType"></param>
        /// <returns></returns>
        private IRepository CreateDefaultRepository(Type entityType)
        {
            //var module = RuntimeEnvironment.Modules.FirstOrDefault(o => o.Key == entityType.Namespace);
            //if (module.Value == null)
            //    Error.NotSupported(StringID.CantFoundModule.ToLocalStringFormat(entityType.Name));
            //Type type = module.Value.DefaultEntityRepositoryType.MakeGenericType(entityType);
            //return System.Activator.CreateInstance(type) as IRepository;
            Type repType = RepositoryFactoryHost.DefaultEntityRepositoryType.MakeGenericType(entityType);
            return System.Activator.CreateInstance(repType) as IRepository;
        }

        #endregion

        #region IEntityRepository Members

        IRepository IRepositoryFactory.Create(Type entityType)
        {
            return this.Find(entityType);
        }

        IRepository<T> IRepositoryFactory.GetRepository<T>()
        {
            var result = this.Find(typeof(T));
            if (result == null)
                return null;
            return result as IRepository<T>;
        }
        #endregion
    }

    /// <summary>
    /// 这个类主要用于依赖注入
    /// </summary>
    public static class RepositoryFactoryHost
    {
        /// <summary>
        /// 未来该类为依赖注入的ILazyProviderFactory
        /// </summary>
        public static IRepositoryFactory Factory = RepositoryFactory.Instance;
        public static Type DefaultEntityRepositoryType;
    }
    /// <summary>
    /// 类型的比较器
    /// </summary>
    public class TypeNameComparer : IComparer<Type>
    {
        public static readonly TypeNameComparer Instance = new TypeNameComparer();

        private TypeNameComparer() { }

        /// <summary>
        /// TypeNameComparer 先尝试使用Name来比较，如果一样，再使用NameSpace进行比较。
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        int IComparer<Type>.Compare(Type x, Type y)
        {
            var result = x.Name.CompareTo(y.Name);
            if (result == 0)
            {
                result = x.Namespace.CompareTo(y.Namespace);
            }
            return result;
        }
    }
}
